package mobi.jenkinsci.server;
import com.google.inject.*;
import com.google.inject.servlet.GuiceFilter;
import com.google.inject.servlet.GuiceServletContextListener;
import com.google.inject.servlet.ServletModule;
import lombok.extern.slf4j.Slf4j;
import mobi.jenkinsci.server.realm.AccessService;
import org.eclipse.jetty.security.*;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import javax.servlet.DispatcherType;
import java.util.EnumSet;
@Slf4j
public class GuiceServer {
private static final int DEFAULT_HTTP_PORT = 8080;
private final int jettyServerPort;
private final ServletModule servletModule;
private Server jettyServer;
private boolean started;
private Injector injector;
public GuiceServer(int serverPort, ServletModule servletModule) {
this.jettyServerPort = serverPort;
this.servletModule = servletModule;
}
public GuiceServer(ServletModule servletModules) {
this(DEFAULT_HTTP_PORT, servletModules);
}
public Injector initInjector(Module... extraModules) {
Module[] modules = new Module[extraModules.length + 1];
modules[0] = servletModule;
System.arraycopy(extraModules, 0, modules, 1, extraModules.length);
injector = Guice.createInjector(modules);
return injector;
}
public Injector getInjector() {
if (injector == null) {
return initInjector();
} else {
return injector;
}
}
public void start() throws Exception {
if (!started) {
jettyServer = getJettyServer(getInjector());
jettyServer.start();
started = true;
}
}
public void stop() throws Exception {
if (started) {
jettyServer.stop();
jettyServer = null;
started = false;
}
}
private Server getJettyServer(final Injector servletInjector) {
Server jettyServer = new Server(jettyServerPort);
final ServletContextHandler app = new ServletContextHandler();
GuiceFilter filter = servletInjector.getInstance(GuiceFilter.class);
app.addFilter(new FilterHolder(filter), "/*", EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC));
app.addEventListener(new GuiceServletContextListener() {
@Override
protected Injector getInjector() {
return servletInjector;
}
});
// This servlet is never used but apparently Jetty requires at least one Servlet to run
final ServletHolder ds = app.addServlet(DefaultServlet.class, "/");
ds.setInitParameter("dirAllowed", "false");
ds.setInitParameter("redirectWelcome", "false");
ds.setInitParameter("useFileMappedBuffer", "false");
ds.setInitParameter("gzip", "true");
app.setSecurityHandler(getConfiguredSecurityHandler(app));
jettyServer.setHandler(app);
return jettyServer;
}
private ConstraintSecurityHandler getConfiguredSecurityHandler(ServletContextHandler app) {
try {
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
securityHandler.setLoginService(getInstance(LoginService.class));
securityHandler.setIdentityService(getInstance(IdentityService.class));
for (ConstraintMapping constraint : getInstance(AccessService.class).getAccessConstraints()) {
securityHandler.addConstraintMapping(constraint);
}
Authenticator authenticator = getInstance(Authenticator.class);
authenticator.setConfiguration(securityHandler);
securityHandler.setAuthenticator(authenticator);
return securityHandler;
} catch (ConfigurationException | ProvisionException e) {
GuiceServer.log.debug("No LoginService configured in Guice bindings: " + e.getMessage());
return null;
}
}
public <T> T getInstance(Class<T> clazz) {
return getInjector().getInstance(clazz);
}
}